#include "enblendrenderer.h"
#include "inputimage.h"
#include "stitcher.h"
#include "commandlineoptions.h"
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
#include <opencv/cv.h>
#include <Magick++.h>
#include <cstdlib>
#include "enblendrenderer.h"
#include "imageset.h"

using namespace std;
using namespace cshotel;
using namespace boost;
using namespace boost::filesystem;
using namespace cv;
using namespace Magick;

EnblendRenderer::EnblendRenderer(shared_ptr<ImageSet> imgs) : imageSet(imgs){}

void EnblendRenderer::render() {
    dout("Rendering output ..." << endl);
    InitializeMagick(0);
    
    ImageSet::BoundingBox boundingBox = imageSet->getBoundingBox();
    dout("    total bounding box: corner (" << boundingBox.minX << ", " << boundingBox.minY << "), size (" << boundingBox.width << ", " << boundingBox.height << ")" << endl);
    
    foreach(shared_ptr<InputImage> img, imageSet->getImages()) {
        dout("    transforming image " << img->getId() << endl);
        
        string originalFileName = img->getName();
        string transformedFileName = getTransformedVersionFileName(path(originalFileName).filename());
        
        int sizeX = ceil(boundingBox.width);
        int sizeY = ceil(boundingBox.height);
        dout("        offset: (" << -boundingBox.minX << ", " << -boundingBox.minY << ")  size: (" << sizeX << ", " << sizeY << ")" << endl);
        dout("        rendering ..." << endl);
        
        // generate the enblend input files
        Mat translation = (Mat_<double>(3, 3) << 1, 0, -boundingBox.minX, 0, 1, -boundingBox.minY, 0, 0, 1);
        shared_ptr<Mat> imageFile = img->getImage();
        
        // since OpenCV does not support an alpha channel, we use pink instead
        // we will later replace pink with a real alpha channel
        Mat dest(sizeY, sizeX, CV_8UC3, OpenCVMaskColor);
        warpPerspective(*imageFile, dest, translation * img->getGlobalHomography(), Size(sizeX, sizeY), INTER_LANCZOS4, BORDER_TRANSPARENT);
        
        dout("        adding alpha channel ..." << endl);
        Image output(Geometry(sizeX, sizeY), GraphicsMagickBackgroundColor);
        Pixels view(output);
        PixelPacket *pixels = view.get(0, 0, sizeX, sizeY);
        for(int yy = 0; yy < sizeY; ++yy) {
            const unsigned char* row = dest.ptr<unsigned char>(yy);
            for(int xx = 0; xx < sizeX; ++xx) {
                Color current(row[xx * 3 + 2], row[xx * 3 + 1], row[xx * 3]);
                if(current != GraphicsMagickMaskColor) {
                    *pixels = Color(row[xx * 3 + 2], row[xx * 3 + 1], row[xx * 3]);
                }
                ++pixels;
            }
        }
        
        view.sync();
        dout("        writing to " << transformedFileName << " ..." << endl);
        output.write(transformedFileName.c_str());
        img->setTransformedVersion(transformedFileName);
    }
    
    if(CommandLineOptions::getInstance().getOutputFormat() != CommandLineOptions::TransformedFiles) {
        string enblendCommand = buildEnblendCommand();
        dout("    running enblend: " << enblendCommand << endl);
        std::system(enblendCommand.c_str());
        
        if(CommandLineOptions::getInstance().getOutputFormat() == CommandLineOptions::SeaDragon) {
            string deepzoomcommand = buildDeepZoomConverterCommand();
            dout("    running DeepZoom converter: " << deepzoomcommand << endl);
            std::system(deepzoomcommand.c_str());
        }
        
        if(CommandLineOptions::getInstance().getCreateThumbnail()) {
            dout("    creating thumbnail " << getThumbnailFileName() << "..." << endl);
            createThumbnail();
        }
    }
}

string EnblendRenderer::buildEnblendCommand() {
    ostringstream command;
    command << "enblend ";
    command << CommandLineOptions::getInstance().getEnblendOptions();
    command << " -o " << getPanoramaFileName();
    foreach(shared_ptr<InputImage> img, imageSet->getImages()) {
        command << " " << img->getTransformedVersionFileName();
    }
    return command.str();
}

const int EnblendRenderer::maskRed;
const int EnblendRenderer::maskGreen;
const int EnblendRenderer::maskBlue;
const Color EnblendRenderer::GraphicsMagickMaskColor(maskRed, maskGreen, maskBlue, 255);
const Color EnblendRenderer::GraphicsMagickBackgroundColor(0, 0, 0, 255);
const Scalar EnblendRenderer::OpenCVMaskColor(maskBlue, maskGreen, maskRed, 255);
